#include "ctrl.h"


static int isOne = 0;
static int ctrl_major = 240;
static int ctrl_minor = 0;
static int irq;
static struct class* __class = NULL;
static MobileCtrlDevice* __dev = NULL;
//FOR GPIO
//static void *vGPIOCTRL , *vGPIODATA, *vGPIOPUD;
//#define GPIOCTRL (*(volatile unsigned int *) vGPIOCTRL)
//#define GPIODATA (*(volatile unsigned int *) vGPIODATA)
//#define GPIOPUD  (*(volatile unsigned int *) vGPIOPUD)

int ctrl_open(struct inode* inode, struct file* filp);
int ctrl_release(struct inode* inode, struct file* filp);
long ctrl_ioctl(struct file* filp, unsigned int cmd, unsigned long arg);
ssize_t ctrl_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos);
ssize_t ctrl_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos);


#define TAG "[HXD019]"

#define LOGD(fmt, arg...) pr_info(TAG "[%s %d]" fmt, __func__, __LINE__, ##arg)
#define LOGI(fmt, arg...) pr_info(TAG "[%s %d]" fmt, __func__, __LINE__, ##arg)
#define LOGW(fmt, arg...) pr_warn(TAG "[%s %d]" fmt, __func__, __LINE__, ##arg)
#define LOGE(fmt, arg...) pr_err(TAG "[%s %d]" fmt, __func__, __LINE__, ##arg)

struct file_operations __fops =
{
	.owner = THIS_MODULE,
	.open = ctrl_open,
	.release = ctrl_release,
	.unlocked_ioctl = ctrl_ioctl,
	.read = ctrl_read,
	.write = ctrl_write,
};

int ctrl_open(struct inode* inode, struct file* filp)
{
	MobileCtrlDevice* dev;
	dev = container_of(inode->i_cdev, MobileCtrlDevice, dev);

	LOGI("enter\n");

	filp->private_data = dev;
	return 0;
}

int ctrl_release(struct inode* inode, struct file* filp)
{
	LOGI("enter\n");
	return 0;
}

void dump_data(char *buf, int count, const char *msg)
{
	int i = 0;
	pr_debug("%s:\n{", msg);
	for(i=0; i<count; i++){
		pr_debug("0x%02x ", buf[i]);
	}
	pr_debug("}\n");
}


long ctrl_ioctl(/*struct inode* inode, */struct file* filp, unsigned int cmd, unsigned long arg)
{
	//MobileCtrlDevice* dev = filp->private_data;
	//printk("[Infrared] ctrl_ioctl enter!!!!!\n");

	LOGI("cmd = %d\n", cmd);
	switch(cmd)
	{
	//case CMD_CTRL_LEARN:
	//	gpio_learn();
	//	break;
	case CMD_CTRL_CSN_H:
		gpio_csn_output();
		gpio_csn_hight();
		break;

	case CMD_CTRL_CSN_L:
		gpio_csn_output();
		gpio_csn_low();
		break;

	case CMD_CTRL_DAT_H:
		gpio_dat_output();
		gpio_dat_hight();
		break;

	case CMD_CTRL_DAT_L:
		gpio_dat_output();
		gpio_dat_low();
		break;

	case CMD_CTRL_CLK_H:
		gpio_clk_output();
		gpio_clk_hight();
		break;

	case CMD_CTRL_CLK_L:
		gpio_clk_output();
		gpio_clk_low();
		break;

	default:
		LOGE("unknow cmd = default\n");
		return -ENOTTY;
	}
	gpio_csn_input();
	return 0;
}

ssize_t ctrl_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos)
{
	int i = 0, retry = 100;
	size_t _count = 0;
	char ch;
	char checksum;
	MobileCtrlDevice* dev = filp->private_data;

	LOGI("count = %lu\n", count);

	//if(down_interruptible(&(dev->sem)))
	//{
	//	return -ERESTARTSYS;
	//}
	if(count <= 0)
	{
		//up(&(dev->sem));
		return 0;
	}
	if(count > sizeof(dev->val_in) - 1)
	{
		_count = sizeof(dev->val_in) - 1;
	}
	else
	{
		_count = count;
	}
	memset(dev->val_in, '\0', MAX_BUFFER_COUNT);
	msleep(100);
	while (gpio_csn_status() == 0)
	{
		//up(&(dev->sem));
		LOGW("Ctrl: 0, busy retry=%d\n", retry);
		msleep(100);
		if(retry-- < 0)
			break;
	}
	gpio_open();
	udelay(DELAY_TIME_US);
	gpio_clk_low();
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	gpio_clk_hight();
	mdelay(DELAY_TIME_MS);
	gpio_start();
	udelay(DELAY_TIME_US);

	gpio_write_data(0x30);
	udelay(DELAY_TIME_US);
	if (isOne == 0x00)
	{
		gpio_write_data(0x52);
	}
	else
	{
		gpio_write_data(0x62);
	}
	udelay(DELAY_TIME_US);

	gpio_start();
	udelay(DELAY_TIME_US);
	gpio_write_data(0x31);
	udelay(DELAY_TIME_US);
	gpio_read_data(&ch);
	if(ch != 0x00)
	{
		gpio_stop();
		udelay(DELAY_TIME_US);
		gpio_close();
		udelay(DELAY_TIME_US);
		//up(&(dev->sem));
		LOGE("ch != 0x00\n");
		return -1;
	}
	*(dev->val_in) = ch;
	if (isOne == 0x00)
	{
		checksum = 0xB3;
	}
	else
	{
		checksum = 0xC3;
	}

	for(i = 1; i < _count; i++)
	{
		gpio_read_data(&ch);
		udelay(DELAY_TIME_US);
		*(dev->val_in + i) = ch;
		checksum += ch;
	}
	gpio_read_data(&ch);
	udelay(DELAY_TIME_US);
	gpio_stop();
	udelay(DELAY_TIME_US);
	gpio_close();
	udelay(DELAY_TIME_US);
	LOGD("dump first ten read data:");
	for(i=0; i< 10; i++){
		printk("%02x ", dev->val_in[i]);
	}
	printk("\n");

	if(ch != checksum)  //ÆäËûÏîÄ¿Ã»×öÐ£Ñé£¬Èç¹û¶ÁÊý¾ÝÍ¨²»¹ý£¬¿ÉÒÔ¹Øµô±¾º¯Êý£»
	{
		LOGW("checksum mismatch: ch=%x, checksum=%x\n", ch, checksum);
		if (isOne == 0){
			return -EFAULT;
		}
	}

	if(copy_to_user(buf, &(dev->val_in), _count))
	{
	//	up(&(dev->sem));
		LOGE("Ctrl:[ -1]\n");
		return -EFAULT;
	}
	//up(&(dev->sem));
	LOGI("Ctrl:[OK], count=%lu\n", _count);
	return _count;
}

ssize_t ctrl_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos)
{
	int i = 0;
	size_t _count = 0;
	MobileCtrlDevice* dev = filp->private_data;
	LOGI("count=%lu\n", count);

	//for(i=0;i<strlen(buf);i++)
	//	printk("buf[%d]=0x%x  ",i,buf[i]);
	//printk("\n");
	/*if(down_interruptible(&(dev->sem)))
	{
		return -ERESTARTSYS;
	}*/
	if(count > sizeof(dev->val_out) - 1)
	{
		_count = sizeof(dev->val_out) - 1;
	}
	else
	{
		_count = count;
	}
	if(copy_from_user(&(dev->val_out), buf, _count))
	{
		//up(&(dev->sem));
		LOGE("copy_from_user fail\n");
		return -EFAULT;
	}
	dump_data(dev->val_out, _count, "ctrl_write");
	if (dev->val_out[0] == 0x30 && dev->val_out[1] == 0x10 && dev->val_out[2] == 0x40)
	{

		isOne = 0;
	}
	else if (dev->val_out[0] == 0x30 && dev->val_out[1] == 0x20 && dev->val_out[2] == 0x50)
	{
		isOne = 1;
	}
	LOGI("isOne = %d\n", isOne);
	gpio_open();
	udelay(DELAY_TIME_US);
	gpio_clk_low();
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	gpio_clk_hight();

	mdelay(DELAY_TIME_MS);
	gpio_start();
	udelay(DELAY_TIME_US);
	for(i = 0; i < _count; i++)
	{
		gpio_write_data(dev->val_out[i]);
		udelay(DELAY_TIME_US);
	}
	udelay(DELAY_TIME_US);
	gpio_stop();
	udelay(DELAY_TIME_US);
	gpio_close();
	udelay(DELAY_TIME_US);
	memset(dev->val_out, '\0', MAX_BUFFER_COUNT);
	//up(&(dev->sem));
	return _count;
}

static int __setup_cdev(MobileCtrlDevice* dev)
{
	int retval;
	dev_t devno = MKDEV(ctrl_major, ctrl_minor);
	memset(dev, 0, sizeof(MobileCtrlDevice));
	cdev_init(&(dev->dev), &__fops);
	dev->dev.owner = THIS_MODULE;
	dev->dev.ops = &__fops;
	retval = cdev_add(&(dev->dev), devno, 1);
	if(retval)
	{
		return retval;
	}
	//init_MUTEX(&(dev->sem));
	memset(dev->val_in, '\0', sizeof(dev->val_in));
	memset(dev->val_out, '\0', sizeof(dev->val_out));
	return 0;
}


int ctrl_init(void)
{
	int retval = 0;

	dev_t dev = MKDEV(ctrl_major, 0);

	printk("Infrared ctrl_init enter....\n");

	/*if (ctrl_major > 0)
	{
		printk("Infrared ctrl_init ctrl_major > 0.\n");
		retval = register_chrdev_region(dev, 1, CTRL_DEVICE_NODE_NAME);
	}
	else
	{*/
		printk("Infrared ctrl_init ctrl_major < 0.\n");
		retval = alloc_chrdev_region(&dev, 0, 1, CTRL_DEVICE_NODE_NAME);
	//}
	printk( "######### Infrared ctrl_init  111111111111111 ######\n");
	if(retval < 0)
	{
		printk("Infrared ctrl_init failed retval < 0.\n");
		return retval;
	}
	ctrl_major = MAJOR(dev);
	ctrl_minor = MINOR(dev);
	printk( "######### Infrared ctrl_init  22222222222222222 ######\n");
	__dev = kmalloc(sizeof(MobileCtrlDevice), GFP_KERNEL);
	if(!__dev)
	{
		unregister_chrdev_region(MKDEV(ctrl_major, ctrl_minor), 1);
		printk("Infrared ctrl_init unregister_chrdev_region.\n");
		return -ENOMEM;
	}
	retval = __setup_cdev(__dev);
	printk( "######### Infrared ctrl_init  33333333333333333333 ######\n");
	if(retval)
	{
		kfree(__dev);
		printk("Infrared ctrl_init kfree.\n");
		return retval;
	}
	printk( "######### Infrared ctrl_init  444444444444444444444 ######\n");
	__class = class_create(THIS_MODULE, CTRL_DEVICE_CLASS_NAME);
	if(IS_ERR(__class))
	{
		retval = PTR_ERR(__class);
		cdev_del(&(__dev->dev));
		printk("Infrared ctrl_init cdev_del.\n");
		return retval;
	}
	printk( "######### Infrared ctrl_init  555555555555555555 ######\n");
	device_create(__class, NULL, dev, "%s", CTRL_DEVICE_FILE_NAME);
	printk("Infrared ctrl_init gpio_init.\n");

	printk("Infrared Succedded To Initialize Mobile Ctrl Device.\n");
	return 0;
}

void ctrl_exit(void)
{
	dev_t devno = MKDEV(ctrl_major, ctrl_minor);

	printk("Infrared ctrl_exit.\n");
	if(__class)
	{
		device_destroy(__class, MKDEV(ctrl_major, ctrl_minor));
		class_destroy(__class);
	}
	if(__dev)
	{
		cdev_del(&(__dev->dev));
		kfree(__dev);
	}
	unregister_chrdev_region(devno, 1);
}


#if 0
void gpio_init(void)
{


	gpio_dat_output();
	gpio_clk_output();
	gpio_csn_input();

	gpio_dat_hight();
	gpio_clk_hight();
}
#endif
void gpio_open(void)
{
	gpio_dat_output();
	gpio_clk_output();
	gpio_clk_hight();
	gpio_dat_hight();
}

void gpio_close(void)
{
	//printk("[Infrared] gpio_close B\n");
	gpio_dat_output();
	gpio_clk_output();
	gpio_clk_hight();
	gpio_dat_hight();
}

void gpio_start(void)
{
	//printk("[Infrared] gpio_start B\n");
	gpio_dat_output();
	gpio_clk_output();
	gpio_clk_hight();
	gpio_dat_hight();

	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	gpio_dat_low();
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	gpio_clk_low();
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
}


void gpio_stop(void)
{
	//printk("[Infrared] gpio_stop B\n");
	gpio_dat_output();
	gpio_clk_output();
	gpio_clk_low();
	gpio_dat_low();
	udelay(DELAY_TIME_US);
	gpio_clk_hight();
	udelay(DELAY_TIME_US);
	gpio_dat_hight();
	udelay(DELAY_TIME_US);
}


int gpio_check_ack(void)
{

	int ACK_Sign;

	//printk("[Infrared] gpio_check_ack B\n");

	gpio_dat_input();
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	gpio_clk_hight();
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	ACK_Sign = gpio_dat_status();
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	gpio_clk_low();
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	return ACK_Sign;
}

void gpio_send_ack(void)
{
	//printk("[Infrared] gpio_send_ack B\n");
	gpio_dat_output();
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);

	gpio_clk_hight();
	udelay(DELAY_TIME_US);
	gpio_clk_low();
}

int gpio_read_data(char* data)
{

	char readdata = 0;
	char i = 8;

	//printk("[Infrared] gpio_read_data B\n");

	gpio_dat_input();
	while (i--)
	{
		readdata <<= 1;
		gpio_clk_hight();
		udelay(DELAY_TIME_US);
		readdata |= gpio_dat_status();
		gpio_clk_low();
		udelay(DELAY_TIME_US);
		udelay(DELAY_TIME_US);
	}
	gpio_clk_low();
	udelay(DELAY_TIME_US);
	*data = readdata;
	gpio_send_ack();

	return 0;
}

int gpio_write_data(char data)
{

	int Data_Bit,ACK_Sign;
	int i;
	//printk("[Infrared] gpio_write_data B\n");

	gpio_dat_output();
	gpio_clk_low();
	udelay(DELAY_TIME_US);
	for(i = 7; i >= 0; i--)
	{
		udelay(DELAY_TIME_US);
		Data_Bit = (data >> i) & 0x01;
		if(Data_Bit)
		{
			gpio_dat_hight();
		}
		else
		{
			gpio_dat_low();
		}
		udelay(DELAY_TIME_US);
		gpio_clk_hight();
		udelay(DELAY_TIME_US);
		gpio_clk_low();
	}
	ACK_Sign = gpio_check_ack();
	return ACK_Sign;
}

void gpio_learn(void)
{
	//printk("[Infrared] gpio_learn B\n");
	gpio_open();
	udelay(DELAY_TIME_US);
	gpio_clk_low();
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	gpio_clk_hight();
	mdelay(DELAY_TIME_MS);
	gpio_start();
	udelay(DELAY_TIME_US);
	gpio_write_data(0x30);
	udelay(DELAY_TIME_US);
	gpio_write_data(0x10);
	udelay(DELAY_TIME_US);
	gpio_write_data(0x40);
	udelay(DELAY_TIME_US);
	udelay(DELAY_TIME_US);
	gpio_stop();
	udelay(DELAY_TIME_US);
	gpio_close();
	udelay(DELAY_TIME_US);
}

//for dat pin
void gpio_dat_input(void)
{
	//printk("[Infrared] gpio_dat_input B\n");
				 //7 6  5 4  3 2  1 0
	//GPIOPUD = 0x2255;//0010 0010 1010 1010
	//udelay(DELAY_TIME_US);
	//mt_set_gpio_mode(69,0);
	//mt_set_gpio_dir(69,0);//in

	gpio_direction_input(GPIO_I2C_SDA);

	//GPIOCTRL &= ~(GPIO_OUTPUT << PDAT * 4);

}

void gpio_dat_output(void)
{
	//printk("[Infrared] gpio_dat_output B\n");
		 //7 6  5 4  3 2  1 0
	//GPIOPUD = 0x5255;//1010 0010 1010 1010
	udelay(DELAY_TIME_US);
	//mt_set_gpio_mode(69,0);
	//mt_set_gpio_dir(69,1);//out
	gpio_direction_output(GPIO_I2C_SDA,1);
	//GPIOCTRL |= (GPIO_OUTPUT << PDAT * 4);
}

void gpio_dat_low(void)
{
	//mt_set_gpio_mode(69,0);
	//mt_set_gpio_out(69,0);
	gpio_direction_output(GPIO_I2C_SDA,0);
	//GPIODATA &= ~(1 << PDAT);
}

void gpio_dat_hight(void)
{
	//mt_set_gpio_mode(69,0);
	//mt_set_gpio_out(69,1);
	gpio_direction_output(GPIO_I2C_SDA,1);
	//GPIODATA |= (1 << PDAT);
}

int gpio_dat_status(void)
{
	//return infrared_get_sda_value();//((GPIODATA >> PDAT) & 0x01);
	//return __gpio_get_value(GPIO_I2C_SDA);
	return gpio_get_value(GPIO_I2C_SDA);
}

//for clk pin
void gpio_clk_input(void)
{
	//mt_set_gpio_mode(71,0);
	//mt_set_gpio_dir(71,0);//in
	gpio_direction_input(GPIO_I2C_SCL);
	//GPIOCTRL &= ~(GPIO_OUTPUT << PCLK * 4);

}

void gpio_clk_output(void)
{
	//mt_set_gpio_mode(71,0);
	//mt_set_gpio_dir(71,1);//out
	gpio_direction_output(GPIO_I2C_SCL,1);
	//GPIOCTRL |= (GPIO_OUTPUT << PCLK * 4);
}

void gpio_clk_low(void)
{
	//mt_set_gpio_mode(71,0);
	//mt_set_gpio_out(71,0);
	gpio_direction_output(GPIO_I2C_SCL,0);
	//GPIODATA &= ~(1 << PCLK);

}

void gpio_clk_hight(void)
{
	//mt_set_gpio_mode(71,0);
	//mt_set_gpio_out(71,1);
	gpio_direction_output(GPIO_I2C_SCL,1);
	//GPIODATA |= (1 << PCLK);

}

//for csn pin
int gpio_clk_status(void)
{
//	return mt_get_gpio_in(57);//((GPIODATA >> PCSN) & 0x01);
	//return __gpio_get_value(GPIO_I2C_SCL);
	return gpio_get_value(GPIO_I2C_SCL);
}
void gpio_csn_input(void)
{
	//mt_set_gpio_mode(57,0);
	//mt_set_gpio_dir(57,0);//in
	gpio_direction_input(GPIO_I2C_BUSY);
	//GPIOCTRL &= ~(GPIO_OUTPUT << PCSN * 4);

}

void gpio_csn_output(void)
{
	//mt_set_gpio_mode(57,0);
	//mt_set_gpio_dir(57,1);//out
	gpio_direction_output(GPIO_I2C_BUSY,0);
	//GPIOCTRL |= (GPIO_OUTPUT << PCSN * 4);

}

void gpio_csn_low(void)
{
	//mt_set_gpio_mode(57,0);
	//mt_set_gpio_out(57,0);
	gpio_direction_output(GPIO_I2C_BUSY,0);
	//GPIODATA &= ~(1 << PCSN);

}

void gpio_csn_hight(void)
{
	//mt_set_gpio_mode(57,0);
	//mt_set_gpio_out(57,1);
	gpio_direction_output(GPIO_I2C_BUSY,1);
	//GPIODATA |= (1 << PCSN);

}

int gpio_csn_status(void)
{
	//return infrared_get_busy_value();//((GPIODATA >> PCSN) & 0x01);
	return __gpio_get_value(GPIO_I2C_BUSY);
}

static irqreturn_t hxd019_isr(int irq, void *dev_id)
{
	LOGI("+++");	
	return IRQ_HANDLED;
}
static int hxd019_probe(struct platform_device *pdev)
{
	struct device * dev = &pdev->dev;
	int ret = 0;

	GPIO_I2C_SDA = of_get_named_gpio(dev->of_node, "sda-gpio", 0);
	if(GPIO_I2C_SDA == -EPROBE_DEFER){
		return -EPROBE_DEFER;
	}
	if(gpio_is_valid(GPIO_I2C_SDA)){
		ret = devm_gpio_request_one(dev, GPIO_I2C_SDA, GPIOF_OUT_INIT_HIGH, "sda-gpio");
		if(ret != 0){
			return ret;
		}
	}

	GPIO_I2C_SCL = of_get_named_gpio(dev->of_node, "scl-gpio", 0);
	if(GPIO_I2C_SCL == -EPROBE_DEFER){
		return -EPROBE_DEFER;
	}
	if(gpio_is_valid(GPIO_I2C_SCL)){
		ret = devm_gpio_request_one(dev, GPIO_I2C_SCL, GPIOF_OUT_INIT_HIGH, "scl-gpio");
		if(ret != 0){
			return ret;
		}
	}

	GPIO_I2C_BUSY = of_get_named_gpio(dev->of_node, "busy-gpio", 0);
	if(GPIO_I2C_BUSY == -EPROBE_DEFER){
		return -EPROBE_DEFER;
	}
	if(gpio_is_valid(GPIO_I2C_BUSY)){
		irq = gpio_to_irq(GPIO_I2C_BUSY);
		ret = devm_request_irq(dev, irq, hxd019_isr, IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING , "IR", NULL);
		if(ret){
			LOGI("request_irq fail\n");
		}
		ret = devm_gpio_request_one(dev, GPIO_I2C_BUSY, GPIOF_IN, "busy-gpio");
		
		if(ret != 0){
			return ret;
		}
	}

	dev_info(dev, "ctrl_sda_gpio_number %d\n", GPIO_I2C_BUSY);
	dev_info(dev, "ctrl_scl_gpio_number %d\n", GPIO_I2C_SCL);
	dev_info(dev, "ctrl_busy_gpio_number %d\n", GPIO_I2C_BUSY);
	ctrl_init();

	return 0;
}
static int hxd019_remove(struct platform_device *pdev)
{
	ctrl_exit();
  	return 0;
}

static const struct of_device_id hxd019_of_match[] = {
  {.compatible = "hxd019", },
  {},
};

static struct platform_driver hxd019_driver = {
  .driver     = {
      .name   = "hxd019",
      .of_match_table = of_match_ptr(hxd019_of_match),
  },
  .probe      = hxd019_probe,
  .remove     = hxd019_remove,
};
//module_init(ctrl_init);
//late_initcall(ctrl_init);
module_platform_driver(hxd019_driver)
//module_exit(ctrl_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Mobile Ctrl Driver");
MODULE_AUTHOR("ET.Song QQ:87224511");


